package com.eascs.web.o2o.scheduler.filter;

import java.lang.reflect.Method;
import java.util.Stack;

import javax.inject.Inject;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.eascs.app.log.Logger;
import com.eascs.app.log.LoggerFactory;
import com.eascs.web.o2o.scheduler.filter.OpTransaction.OpType;

@Aspect
@Component
public class MultiTransactionalAspect {

	private Logger logger = LoggerFactory.getLogger(getClass());

	ApplicationContext ApplicationContext;

	@Inject
	PlatformTransactionManager transactionManager;
	@Inject
	PlatformTransactionManager transactionManagerDbForCrm;
	
	/**
	 * ThreadLocal：
	 * 1.spring单例模式下，使用ThreadLocal创建变量副本，处理并发问题。
	 * 2.spring事务在stack中是先进先出（lifo）模式，所以必须为每一个线程提供独自的stack来存放事务。这样才不会出现事务提交混乱的问题。
	 * 3.每个ThreadLocal副本，都是前后一致的（set,get一致）
	 */
	private ThreadLocal<Stack<DataSourceTransactionManager>> dataSourceTransactionManagerLocal = new ThreadLocal<Stack<DataSourceTransactionManager>>();
	private ThreadLocal<Stack<TransactionStatus>> transactionStatuLocal = new ThreadLocal<Stack<TransactionStatus>>();

	private void init() {
		Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>();
		dataSourceTransactionManagerLocal.set(dataSourceTransactionManagerStack);
		Stack<TransactionStatus> transactionStatusStack = new Stack<TransactionStatus>();
		transactionStatuLocal.set(transactionStatusStack);
	}

	@Pointcut("@annotation(com.eascs.web.o2o.scheduler.filter.OpTransaction)")
	public void controllerAspect() {
	}

	@Before("controllerAspect()")
	public void doBefore(JoinPoint joinPoint) {
		init();
		openTransaction(dataSourceTransactionManagerLocal, transactionStatuLocal);
		logger.info("=====SysLogAspect前置通知开始=====");
	}

	@AfterReturning(pointcut = "controllerAspect()")
	public void doAfter(JoinPoint joinPoint) {
		logger.info("=====SysLogAspect后置通知开始=====");
		commit(dataSourceTransactionManagerLocal, transactionStatuLocal);
	}

	@AfterThrowing(value = "controllerAspect()", throwing = "e")
	public void doAfter(JoinPoint joinPoint, Exception e) {
		logger.error("=====SysLogAspect异常通知开始=====",e);
		rollback(dataSourceTransactionManagerLocal, transactionStatuLocal);
	}

	private boolean openTransaction(ThreadLocal<Stack<DataSourceTransactionManager>> dataSourceTransactionManagerLocal,
			ThreadLocal<Stack<TransactionStatus>> transactionStatuLocal) {

		Stack<DataSourceTransactionManager> s1 = dataSourceTransactionManagerLocal.get();
		Stack<TransactionStatus> s2 = transactionStatuLocal.get();

		TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
		s1.push((DataSourceTransactionManager) transactionManager);
		s2.push(transactionStatus);

		TransactionStatus transactionStatusCrm = transactionManagerDbForCrm
				.getTransaction(new DefaultTransactionDefinition());
		s1.push((DataSourceTransactionManager) transactionManagerDbForCrm);
		s2.push(transactionStatusCrm);

		dataSourceTransactionManagerLocal.set(s1);
		transactionStatuLocal.set(s2);

		return true;
	}

	private void commit(ThreadLocal<Stack<DataSourceTransactionManager>> dataSourceTransactionManagerLocal,
			ThreadLocal<Stack<TransactionStatus>> transactionStatuLocal) {
		Stack<DataSourceTransactionManager> s1 = dataSourceTransactionManagerLocal.get();
		Stack<TransactionStatus> s2 = transactionStatuLocal.get();
		while (!s1.isEmpty()) {
			s1.pop().commit(s2.pop());
		}
	}

	private void rollback(ThreadLocal<Stack<DataSourceTransactionManager>> dataSourceTransactionManagerLocal,
			ThreadLocal<Stack<TransactionStatus>> transactionStatuLocal) {
		Stack<DataSourceTransactionManager> s1 = dataSourceTransactionManagerLocal.get();
		Stack<TransactionStatus> s2 = transactionStatuLocal.get();
		while (!s1.isEmpty()) {
			s1.pop().rollback(s2.pop());
		}
	}

	
	//日志处理
    private void handleLog(JoinPoint joinPoint,Exception e) {  
        try {  
            //获得注解  
        	OpTransaction transaction = giveController(joinPoint);  
            if(transaction == null)  
            {  
                return;  
            }  
              
            String signature = joinPoint.getSignature().toString(); // 获取目标方法签名  
            String methodName = signature.substring(signature.lastIndexOf(".") + 1,  
                    signature.indexOf("("));  
  
            String longTemp = joinPoint.getStaticPart().toLongString();  
            String classType = joinPoint.getTarget().getClass().getName();  
  
            Class<?> clazz = Class.forName(classType);  
  
            Method[] methods = clazz.getDeclaredMethods();  
            System.out.println("methodName: " + methodName);  
  
            for (Method method : methods) {  
  
                if (method.isAnnotationPresent(OpTransaction.class)  
                        && method.getName().equals(methodName)) {  
                    //OpLogger logger = method.getAnnotation(OpLogger.class);  
                    String annId = transaction.id();  
                    OpType type = transaction.type();
                    String clazzName = clazz.getName();  
                    System.out.println("clazzName: " + clazzName+ ", methodName: "   
                            + methodName + ", annId: "+ annId + ", type: "+type.toString());  
                }  
            }  
              
        } catch (Exception exp) {  
            logger.error("异常信息:{}", exp.getMessage());  
            exp.printStackTrace();  
           }  
    }  
  
    private static OpTransaction giveController(JoinPoint joinPoint) throws Exception {  
        Signature signature = joinPoint.getSignature();  
        MethodSignature methodSignature = (MethodSignature) signature;  
        Method method = methodSignature.getMethod();  
  
        if (method != null) {  
            return method.getAnnotation(OpTransaction.class);  
        }  
        return null;  
    }  
      
      
    public void insertLogSuccess(JoinPoint jp, OpTransaction logger) {}  
  
    public void writeLogInfo(JoinPoint joinPoint, OpTransaction opLogger)  
            throws Exception, IllegalAccessException {}  
}